#pragma once
#include "common.h"
#include "scheduler.h"
#include "fiber.h"

class mutex
{
public:
	mutex ()
		:is_lock_(false)
	{

	}
	~mutex()
	{
		assert(mutex_owner_ == nullptr);
	}
	bool try_lock ()
	{
		bool expected = false;
		if (is_lock_.compare_exchange_strong(expected,
			true, 
			std::memory_order_seq_cst))
		{
			assert(is_lock_ = true);
			mutex_owner_ = scheduler::current_scheduler()->get_current_schedule_fiber();
			return true;
		}
		return false;
	}
	void lock ()
	{
		auto _fiber = 
			scheduler::current_scheduler()->get_current_schedule_fiber();
		assert(_fiber);
		bool expected = false;
		if (is_lock_.compare_exchange_strong(expected, true, std::memory_order_seq_cst))
		{
			assert(is_lock_ = true);
			mutex_owner_ = _fiber;
			return;
		}
		_fiber->set_status(fiber::status::e_blocking);
		blocking_fibers_.push_back(_fiber);
		_fiber->yield();
	}
	void unlock()
	{
		assert(mutex_owner_ == scheduler::current_scheduler()
			->get_current_schedule_fiber());
		if (blocking_fibers_.empty())
		{
			mutex_owner_.reset();
			is_lock_ = false;
		}
		else
		{
			auto _fiber = blocking_fibers_.front();
			blocking_fibers_.pop_front();
			_fiber->set_status(fiber::status::e_running);
			mutex_owner_ = _fiber;
			_fiber->get_scheduler()->append_fiber(_fiber);
		}
	}
	
private:
	std::deque<fiber::ptr_t> blocking_fibers_;
	fiber::ptr_t mutex_owner_;
	std::atomic_bool is_lock_;
};

struct lock_guard
{
	lock_guard (mutex &mtx)
		:mtx_(mtx)
	{
		mtx_.lock ();
	}
	~lock_guard ()
	{
		mtx_.unlock ();
	}
	mutex &mtx_;
};

struct unique_locker
{
	unique_locker(mutex &mtx)
		:is_lock_(false),
		mtx_(mtx)
	{
		lock();
	}
	~unique_locker()
	{
		if(is_lock_)
			mtx_.unlock();
	}
	bool try_lock()
	{
		if (mtx_.try_lock() == false)
			return false;
		is_lock_ = true;
		return true;
	}
	void lock()
	{
		mtx_.lock();
		is_lock_ = true;
	}
	void unlock()
	{
		mtx_.unlock();
		is_lock_ = false;
	}

	mutex &mtx_;
	bool is_lock_;
};